package com.gmrz.uaf.remote.protocol.v1.processor;

import com.gmrz.service.challengestore.ChallengeStoreException;
import com.gmrz.uaf.common.Constants;
import com.gmrz.uaf.db.DAOException;
import com.gmrz.uaf.policy.verification.PolicyVerificationException;
import com.gmrz.uaf.protocol.v1.json.UAFSchemaBuilder;
import com.gmrz.uaf.protocol.v1.processor.UAFAuthFinishProcessor;
import com.gmrz.uaf.protocol.v1.processor.exception.AuthenticationFailedException;
import com.gmrz.uaf.protocol.v1.processor.exception.PolicyNotFoundException;
import com.gmrz.uaf.protocol.v1.processor.exception.PossibleReplayAttackException;
import com.gmrz.uaf.protocol.v1.processor.exception.UAFErrorCode;
import com.gmrz.uaf.protocol.v1.schema.*;
import com.gmrz.uaf.protocol.v1.validaton.ValidationException;
import com.gmrz.uaf.protocol.v1.validaton.Validator;
import com.gmrz.uaf.remote.plugin.AuthServicePluginManager;
import com.gmrz.uaf.remote.protocol.v1.validation.auth.UAPAuthResponseValidator;
import com.gmrz.uaf.server.ServerContext;
import com.gmrz.uas.plugin.authservice.AuthServiceData;
import com.gmrz.uas.plugin.authservice.AuthServicePlugin;
import com.gmrz.uas.plugin.exception.AuthNoMatchPluginBusinessException;
import com.gmrz.uas.plugin.exception.PluginException;
import com.gmrz.util.Convert;
import com.gmrz.util.CryUtil;
import com.google.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by gongyuqiang on 2017/5/3.
 */
public class UAPAuthFinishProcessor extends UAFAuthFinishProcessor {
    private static final Logger LOG = LogManager.getLogger(UAPAuthFinishProcessor.class);

    AuthServicePluginManager serviceManager;

    @Inject
    public void setServiceManager(AuthServicePluginManager serviceManager) {
        this.serviceManager = serviceManager;
    }

    /**
     * 远程认证
     *
     * @param response
     * @param appID
     * @param hashedUserName
     * @param authType
     * @param transType
     * @throws ValidationException
     * @throws ChallengeStoreException
     * @throws PolicyVerificationException
     * @throws DAOException
     * @throws PossibleReplayAttackException
     * @throws AuthenticationFailedException
     * @throws PolicyNotFoundException
     */
    @Override
    public void process(AuthenticationResponse response, String appID, String hashedUserName, String authType, String transType,String extStr,String functionType)
            throws ValidationException, ChallengeStoreException, PolicyVerificationException, DAOException,
            PossibleReplayAttackException, AuthenticationFailedException, PolicyNotFoundException {
        LOG.info("UAPAuthFinishProcessor Validating authentication response");

        ServerData serverData = response.getOperationHeader().getServerData();
        if (this.getServerConfig().getPerformModel() < Constants.PERFORM_MODEL_LEVEL_2 && !ServerContext.getServerContext().isPerform()) {
            Validator<AuthenticationResponse> authRespValidator = new UAPAuthResponseValidator(this.serverConfig,
                    Constants.Operation.AUTH, appID,transType);
            authRespValidator.validate(response);
            checkForReplayAttack(serverData);
            List<Extension> exts = response.getOperationHeader().getExtensions();
            processExtensions(exts);
            TLSData tlsData = response.getFinalChallengeParams().getTLSData();
            processTLSData(tlsData);
        }
        String plainUserName = serverData.getUserName();

        String policyName = serverData.getPolicyName();
        LOG.info("Auth Response userAliasName[{}], policyname[{}]", plainUserName, policyName);
        AuthenticatorRegistration authentication = response.getAuthentication();
        if (authentication == null) {
            String msg = "No AuthenticatorRegistration found in the registration response for user[" + plainUserName + "], policy["
                    + policyName + "].";

            logAndThrowAFE(msg);
        }
        //判断aaid是否可用
        String aaid = authentication.getAaid();
        Policy p = this.policyManager.getNamedPolicy(authType, appID, transType);
        LOG.info("Policy:" + UAFSchemaBuilder.getGson().toJson(p));
        if (p == null || !isAcceptedAaid(p, aaid)) {
            LOG.error("Requested policy {} is not available on server", "POLICY_" + appID + "_" + authType);
            throw new PolicyNotFoundException(UAFErrorCode.BIZ_POLICY_NOT_FOUND);
        }
        // 生成插件解密key
        String pluginKeyStr = response.getOperationHeader().getApplicationID() + Convert.toBase64(serverData.getChallenge());
        LOG.debug("pluginKeyStr:" + pluginKeyStr);
        List<AuthServiceData> contents = new ArrayList<AuthServiceData>();
        for (Extension ext : authentication.getExtensions()) {
            AuthServiceData content = new AuthServiceData();
            content.setId(ext.getId());
            content.setContent(Convert.fromBase64(ext.getData()));
            content.setKey(pluginKeyStr.getBytes());
            contents.add(content);
        }
        try {
            AuthServicePlugin service = serviceManager.getServicePlugin(aaid, authType);
            String pluginUserName = CryUtil.getSHA(hashedUserName + "_" + appID + "_" + authType + "_" + transType);
            LOG.debug("pluginUserName:" + pluginUserName + "," + hashedUserName + "_" + appID + "_" + authType + "_" + transType);
            service.authenticate(pluginUserName, contents);
        }catch (AuthNoMatchPluginBusinessException e){
            throw e;
        } catch (PluginException e) {
            LOG.error(e.getErrorMsg());
            throw new AuthenticationFailedException(e.getErrorMsg());
        }
    }
}
